﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Reflection;
using System.Windows.Forms;

namespace GBits.ContactUtilites
{
    public class MyContact
    {
        [XmlPropertyAttribute("Id", typeof(string))]
        public string Id { get; set; }
        [XmlPropertyAttribute("FirstName", typeof(string))]
        public string FirstName { get; set; }
        [XmlPropertyAttribute("MiddleName", typeof(string))]
        public string MiddleName { get; set; }
        [XmlPropertyAttribute("LastName", typeof(string))]
        public string LastName { get; set; }
        [XmlPropertyAttribute("Title", typeof(string))]
        public Title Title { get; set; }
        [XmlPropertyAttribute("HomePhone", typeof(string))]
        public string HomePhone { get; set; }
        [XmlPropertyAttribute("CellPhone", typeof(string))]
        public string CellPhone { get; set; }
        [XmlPropertyAttribute("Website", typeof(string))]
        public string Website { get; set; }
        [XmlPropertyAttribute("Favorite", typeof(bool))]
        public bool Favorite { get; set; }

        [XmlPropertyAttribute("Emails", typeof(List<string>))]
        public List<string> Emails { get; set; }

        [XmlPropertyAttribute("Address", typeof(Address))]
        public Address Address { get; set; }
        [XmlPropertyAttribute("Organization", typeof(Organization))]
        public Organization Organization { get; set; }

        [XmlPropertyAttribute("Notes", typeof(string))]
        public string Notes { get; set; }

        [XmlPropertyAttribute("Connected", typeof(List<Related>))]
        public List<Related> Connected { get; set; }

        public MyContact()
        {
            Id = UserOptions.dataDirectory + "\\Temp.gbc";
            Address = new Address();
            Organization = new Organization();
        }

        public MyContact(string xml)
        {
            Address = new Address();
            Organization = new Organization();
            PopulateFromXml(xml);
        }

        public string ToXml()
        {
            MemoryStream ms = new MemoryStream();
            XmlTextWriter tw = new XmlTextWriter(ms, null);

            tw.WriteStartElement("Contact");
            tw.WriteAttributeString("id", this.Id.ToString());
            tw.WriteAttributeString("name", this.FirstName + " " + this.MiddleName + " " + this.LastName);
            tw.WriteAttributeString("organization", this.Organization.OrgName);

            foreach (PropertyInfo pi in this.GetType().GetProperties())
            {
                if (pi.Name.ToLower() == "id" || pi.Name.ToLower() == "name") continue;

                object[] customAttr = pi.GetCustomAttributes(typeof(XmlPropertyAttribute), true);

                if (customAttr != null && customAttr.Length > 0)
                {
                    if (pi.Name.CompareTo("Organization") == 0)
                    {
                        tw.WriteStartElement("Organization");
                        this.Organization.ToXml(tw);
                        tw.WriteEndElement();//Organization
                    }
                    else if (pi.Name.CompareTo("Address") == 0)
                    {
                        tw.WriteStartElement("HomeAddress");
                        this.Address.ToXml(tw);
                        tw.WriteEndElement();//Address
                    }
                    else if (pi.Name.CompareTo("Connected") == 0)
                    {
                        tw.WriteStartElement("Connected");
                        foreach (Related r in this.Connected)
                        {
                            tw.WriteStartElement("Relation");// Relation
                            tw.WriteAttributeString("id", r.Id.ToString().ToUpper());
                            tw.WriteAttributeString("name", r.Name);
                            tw.WriteAttributeString("connection", r.Connection);
                            tw.WriteEndElement();// Relation
                        }
                        tw.WriteEndElement();//Connected
                    }
                    else if (pi.Name.CompareTo("Emails") == 0)
                    {
                        tw.WriteStartElement("Emails");
                        foreach (string s in this.Emails)
                        {
                            tw.WriteStartElement("Email");//Email
                            tw.WriteAttributeString("value", s);
                            tw.WriteEndElement();// Relation
                        }
                        tw.WriteEndElement();//Email
                    }
                    else
                    {
                        tw.WriteStartElement("Property");
                        object val = pi.GetValue(this, null);
                        if (val != null)
                        {
                            tw.WriteAttributeString("name", pi.Name);
                            tw.WriteAttributeString("value", pi.GetValue(this, null).ToString());
                            //tw.WriteAttributeString("value", pi.GetValue(this, null).ToString());
                            tw.WriteEndElement(); // Property
                        }
                    }
                }
            }

            #region Old Code
            //tw.WriteStartElement("Item");
            //tw.WriteAttributeString("id", this.Id.ToString().ToUpper());
            //tw.WriteAttributeString("name", this.Name);
            //tw.WriteAttributeString("nodetype", this.MaterialType.ToString());
            #endregion

            tw.WriteEndElement(); // Item

            tw.Flush();

            ms.Seek(0, SeekOrigin.Begin);
            byte[] buffer = new byte[ms.Length];
            ms.Read(buffer, 0, buffer.Length);
            return Encoding.UTF8.GetString(buffer);
        }

        public void PopulateFromXml(string xml)
        {
            XmlDocument tempDoc = new XmlDocument();
            tempDoc.LoadXml(xml);

            XmlNode root = tempDoc.SelectSingleNode("Contact");

            this.Id = root.Attributes["id"].Value;

            XmlNodeList props = root.SelectNodes("Property");
            XmlNode emailsNode = root.SelectSingleNode("Emails");
            XmlNode homeAddressNode = root.SelectSingleNode("HomeAddress");
            XmlNode organizationNode = root.SelectSingleNode("Organization");
            XmlNode connectionsNode = root.SelectSingleNode("Connected");

            this.Address = new Address(homeAddressNode.OuterXml);
            this.Organization = new Organization(organizationNode.OuterXml);

            foreach (XmlNode node in props)
            {
                foreach (PropertyInfo pi in this.GetType().GetProperties())
                {
                    object[] attributes = pi.GetCustomAttributes(typeof(XmlPropertyAttribute), true);
                    foreach (object attribute in attributes)
                    {
                        XmlPropertyAttribute attr = (XmlPropertyAttribute)attribute;
                        if (attr.Name == node.Attributes["name"].Value)
                        {
                            if (attr.Name.CompareTo("Favorite") == 0)
                            {
                                if (node.Attributes["value"].Value == true.ToString())
                                    pi.SetValue(this, true, null);
                                else
                                    pi.SetValue(this, false, null);
                            }
                            else if (attr.Name.CompareTo("Title") == 0)
                            {
                                switch (node.Attributes["value"].Value)
                                {
                                    case("Mr"):
                                        pi.SetValue(this, Title.Mr, null);
                                        break;
                                    case("Mrs"):
                                        pi.SetValue(this, Title.Mrs, null);
                                        break;
                                    case("Ms"):
                                        pi.SetValue(this, Title.Ms, null);
                                        break;
                                    default:
                                        pi.SetValue(this, Title.None, null);
                                        break;
                                }
                            }
                            else
                            {
                                pi.SetValue(this, node.Attributes["value"].Value, null);
                            }
                        }
                    }
                }
            }
            this.Emails = new List<string>();
            foreach (XmlNode node in emailsNode.SelectNodes("Email"))
            {
                this.Emails.Add(node.Attributes["value"].Value);
            }

            this.Connected = new List<Related>();
            foreach (XmlNode node in connectionsNode.SelectNodes("Relation"))
            {
                Related r = new Related(node.Attributes["id"].Value, node.Attributes["name"].Value, node.Attributes["connection"].Value);
                this.Connected.Add(r);
            }

        }
    }

    public class Address
    {
        [XmlPropertyAttribute("Street", typeof(string))]
        public string Street { get; set; }
        [XmlPropertyAttribute("City", typeof(string))]
        public string City { get; set; }
        [XmlPropertyAttribute("State", typeof(string))]
        public string State { get; set; }
        [XmlPropertyAttribute("Code", typeof(string))]
        public string Code { get; set; }
        [XmlPropertyAttribute("Country", typeof(string))]
        public string Country { get; set; }

        public Address()
        {

        }

        public Address(string xml)
        {
            PopulateFromXml(xml);
        }

        public string ToXml()
        {
            MemoryStream ms = new MemoryStream();
            XmlTextWriter tw = new XmlTextWriter(ms, null);

            //tw.WriteStartElement("Address");
            //tw.WriteAttributeString("id", this.Id.ToString().ToUpper());
            //tw.WriteAttributeString("name", this.Name);
            //tw.WriteAttributeString("nodetype", this.Organization.OrgName);

            foreach (PropertyInfo pi in this.GetType().GetProperties())
            {
                if (pi.Name.ToLower() == "id" || pi.Name.ToLower() == "name") continue;

                object[] customAttr = pi.GetCustomAttributes(typeof(XmlPropertyAttribute), true);

                if (customAttr != null && customAttr.Length > 0)
                {
                    XmlPropertyAttribute attr = (XmlPropertyAttribute)customAttr[0];

                    tw.WriteStartElement("Property");
                    object val = pi.GetValue(this, null);
                    if (val != null)
                    {
                        tw.WriteAttributeString("name", pi.Name);
                        tw.WriteAttributeString("value", pi.GetValue(this, null).ToString());
                        //tw.WriteAttributeString("value", pi.GetValue(this, null).ToString());
                        tw.WriteEndElement(); // Address
                    }
                }
            }

            #region Old Code
            //tw.WriteStartElement("Item");
            //tw.WriteAttributeString("id", this.Id.ToString().ToUpper());
            //tw.WriteAttributeString("name", this.Name);
            //tw.WriteAttributeString("nodetype", this.MaterialType.ToString());
            #endregion

            //tw.WriteEndElement(); // Item

            tw.Flush();

            ms.Seek(0, SeekOrigin.Begin);
            byte[] buffer = new byte[ms.Length];
            ms.Read(buffer, 0, buffer.Length);
            return Encoding.UTF8.GetString(buffer);
        }

        public void ToXml(XmlTextWriter tw)
        {

            foreach (PropertyInfo pi in this.GetType().GetProperties())
            {
                if (pi.Name.ToLower() == "id" || pi.Name.ToLower() == "name") continue;

                object[] customAttr = pi.GetCustomAttributes(typeof(XmlPropertyAttribute), true);

                if (customAttr != null && customAttr.Length > 0)
                {
                    XmlPropertyAttribute attr = (XmlPropertyAttribute)customAttr[0];

                    object val = pi.GetValue(this, null);
                    if (val != null)
                    {
                        tw.WriteStartElement("Address");
                        tw.WriteAttributeString("name", pi.Name);
                        tw.WriteAttributeString("value", pi.GetValue(this, null).ToString());
                        tw.WriteEndElement(); // Address
                    }
                }
            }

        }

        public void PopulateFromXml(string xml)
        {
            XmlDocument tempDoc = new XmlDocument();
            tempDoc.LoadXml(xml);
            XmlElement x = tempDoc.DocumentElement;
            foreach (XmlNode node in x.SelectNodes("Address"))
            {
                foreach (PropertyInfo pi in this.GetType().GetProperties())
                {
                    if (pi.Name == node.Attributes["name"].Value)
                    {
                        pi.SetValue(this, node.Attributes["value"].Value, null);
                    }
                }
            }
        }
    }

    public class Organization
    {
        [XmlPropertyAttribute("OrgName", typeof(string))]
        public string OrgName { get; set; }
        [XmlPropertyAttribute("OrgTitle", typeof(string))]
        public string OrgTitle { get; set; }
        [XmlPropertyAttribute("OrgDepartment", typeof(string))]
        public string OrgDepartment { get; set; }
        [XmlPropertyAttribute("OrgPhone", typeof(string))]
        public string OrgPhone { get; set; }
        [XmlPropertyAttribute("OrgWebsite", typeof(string))]
        public string OrgWebsite { get; set; }

        [XmlPropertyAttribute("OrgAddress", typeof(Address))]
        public Address OrgAddress { get; set; }

        public Organization()
        {
            OrgAddress = new Address();
        }

        public Organization(string xml)
        {
            PopulateFromXml(xml);
        }

        public string ToXml()
        {
            MemoryStream ms = new MemoryStream();
            XmlTextWriter tw = new XmlTextWriter(ms, null);

            foreach (PropertyInfo pi in this.GetType().GetProperties())
            {
                if (pi.Name.ToLower() == "id" || pi.Name.ToLower() == "name") continue;

                object[] customAttr = pi.GetCustomAttributes(typeof(XmlPropertyAttribute), true);

                if (customAttr != null && customAttr.Length > 0)
                {
                    XmlPropertyAttribute attr = (XmlPropertyAttribute)customAttr[0];

                    tw.WriteStartElement("Org");
                    object val = pi.GetValue(this, null);
                    if (val != null)
                    {
                        tw.WriteAttributeString("name", pi.Name);
                        tw.WriteAttributeString("value", pi.GetValue(this, null).ToString());
                        tw.WriteEndElement(); // Address
                    }
                }
            }

            #region Old Code
            //tw.WriteStartElement("Item");
            //tw.WriteAttributeString("id", this.Id.ToString().ToUpper());
            //tw.WriteAttributeString("name", this.Name);
            //tw.WriteAttributeString("nodetype", this.MaterialType.ToString());
            #endregion

            //tw.WriteEndElement(); // Item

            tw.Flush();

            ms.Seek(0, SeekOrigin.Begin);
            byte[] buffer = new byte[ms.Length];
            ms.Read(buffer, 0, buffer.Length);
            return Encoding.UTF8.GetString(buffer);
        }

        public void ToXml(XmlTextWriter tw)
        {
            foreach (PropertyInfo pi in this.GetType().GetProperties())
            {
                if (pi.Name.ToLower() == "id" || pi.Name.ToLower() == "name") continue;

                object[] customAttr = pi.GetCustomAttributes(typeof(XmlPropertyAttribute), true);

                if (customAttr != null && customAttr.Length > 0)
                {
                    XmlPropertyAttribute attr = (XmlPropertyAttribute)customAttr[0];

                    object val = pi.GetValue(this, null);
                    if (val != null)
                    {
                        if (pi.Name == "OrgAddress")
                        {
                            tw.WriteStartElement("OrgAddress");
                            OrgAddress.ToXml(tw);
                            tw.WriteEndElement(); // Address

                        }
                        else
                        {
                            tw.WriteStartElement("Org");
                            tw.WriteAttributeString("name", pi.Name);
                            tw.WriteAttributeString("value", pi.GetValue(this, null).ToString());
                            tw.WriteEndElement(); // Address
                        }
                    }
                }
            }
        }

        public void PopulateFromXml(string xml)
        {
            XmlDocument tempDoc = new XmlDocument();
            tempDoc.LoadXml(xml);
            XmlElement x = tempDoc.DocumentElement;
            XmlNode address = x.SelectSingleNode("OrgAddress");
            this.OrgAddress = new Address(address.OuterXml);
            foreach (XmlNode node in x.SelectNodes("Org"))
            {
                foreach (PropertyInfo pi in this.GetType().GetProperties())
                {
                    if (pi.Name == node.Attributes["name"].Value)
                    {
                        pi.SetValue(this, node.Attributes["value"].Value, null);
                    }
                }
            }
        }

    }

    public struct Related
    {
        public string Id;
        public string Name;
        public string Connection;

        public Related(string id, string name, string connection)
        {
            Id = id;
            Name = name;
            Connection = connection;
        }
    }

    public enum Title
    {
        None = 0,
        Mr,
        Ms,
        Mrs
    }

    public class XmlPropertyAttribute : Attribute
    {
        public string Name { get; set; }
        public Type Type { get; set; }

        public XmlPropertyAttribute(string n, Type t)
        {
            Name = n;
            Type = t;
        }
    }

}
